/*
 *  RL_socket.h
 *  RL_NetLib
 *
 *  Created by Andrew Butcher on 11/09/06.
 *  Copyright 2006 __MyCompanyName__. All rights reserved.
 *
 */

#ifndef NAMESPACE_RLAI_RL_SOCKET_H
#define NAMESPACE_RLAI_RL_SOCKET_H

#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
#include <cassert>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>

namespace prevRLGlue
{


typedef int os_socket;

template <class T> T& endian(T& data, int size)
{
  static const int one = 1;
  static const char little_endian = (*(char*)(&one));

  if (little_endian)
  {
    unsigned char* c = reinterpret_cast<unsigned char*>(&data);
    std::reverse( c, c + sizeof(data) );
  }

  return data;
}
/*
template <class T> T* endian(T* data, int size)
{
  static const int one = 1;
  static const char little_endian = (*(char*)(&one));

  if (little_endian)
  {
    for ( int i = 0; i < size; ++i )
    {
      data[i] = endian(data[i], size);
    }
  }

  return data;
}
*/
class RL_socket
{
 protected:
  os_socket sys_socket;
    
 public:
  RL_socket();
  RL_socket(os_socket socket);
  RL_socket(const RL_socket& rhs){ sys_socket = rhs.get_socket(); } 
  virtual ~RL_socket();
  virtual os_socket get_socket()const ;
  virtual int send_all( const char* msg, int length );
  virtual int recv_all( char* msg, int length );
  virtual int close();
  virtual bool isDataAvailable(); 

  template <class T> int send( T& data, int size )
  {
    int result = 0;
    assert( size == 1 );
    data = endian(data, size);
    result = this->send_all((char*)&data, sizeof(T) * size);
    data = endian(data, size);
    return result;
  }

  template <class T> int send( T* data, int size )
  {
    int result = 0;

    for ( int i = 0; i < size; ++i )
      data[i] = endian(data[i], size);
    
    result = this->send_all((char*)data, sizeof(T) * size);

    for ( int i = 0; i < size; ++i )
      data[i] = endian(data[i], size);

    return result;
  }
  
  template <class T> int recv( T& data, int size )
  {
    assert( size == 1 );
    int result = 0;
    result = this->recv_all((char*)&data, sizeof(T) * size);
    data = endian(data, size);
    return result;
  }
  
  template <class T> int recv( T* data, int size )
  {
    int result = 0;
    result = this->recv_all((char*)data, sizeof(T) * size);
    for ( int i = 0; i < size; ++i )
    {
      data[i] = endian(data[i], size);
    }
    return result;
  }

  void setBlocking(bool f); 
};
  
class RL_client_socket : public RL_socket
{
 protected:
  bool connected;

 public:
  RL_client_socket();
  RL_client_socket(os_socket s, bool connected);
  virtual ~RL_client_socket();
  virtual bool connect_ip( const char* ip, short port );
  virtual bool is_connected();
  virtual int recv_all( char* msg, int length );
  virtual int close();
};

class RL_server_socket : public RL_socket
{		
 public:
  RL_server_socket();
  RL_server_socket(os_socket s);
  virtual ~RL_server_socket();
  virtual void listen( short port );
  virtual RL_socket accept();

  bool isConnected(){return connected;}

protected:
	bool connected; 
};

class RL_socket_set
{
 protected:
  int fds_max;
  fd_set fds_master;
  fd_set fds_read;
 public:
  RL_socket_set();
  virtual ~RL_socket_set();
  
  virtual int select();
  virtual void add(RL_socket& client_connection);
  virtual bool is_ready(RL_socket& client_connection);
};
}

#endif
